package com.guanzi.big_data_mq_process.service;

import com.alibaba.excel.EasyExcel;
import com.guanzi.big_data_mq_process.listener.EasyExcelCacheListener;
import com.guanzi.big_data_mq_process.listener.EasyExcelListener;
import com.guanzi.big_data_mq_process.model.bo.UserOrderBO;
import com.guanzi.big_data_mq_process.model.entity.UserOrder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * (ExcelImportService) 接口
 *
 * @author xuguang
 * @since 2025-06-14 09:34:26
 */
@Slf4j
@Component
public class ExcelImportService {

//    @Autowired
//    private UserOrderListener userOrderListener;

    @Autowired
    @Qualifier("importParsingThreadPool")
    private ThreadPoolExecutor importParsingThreadPool;

    @Autowired
    private IUserOrderService userOrderService;

    public void testImportBigData(MultipartFile file) {
        log.info("开始执行导入...");
        long start = System.currentTimeMillis();
        List<CompletableFuture<?>> parseFutures = new ArrayList<>();
        List<CompletableFuture<?>> dbFutures = new ArrayList<>();

        // 使用缓存了的通用模板监听器，多次调用接口不用老是new
        EasyExcelListener<UserOrder, UserOrderBO> listener = EasyExcelCacheListener.getCacheListener(
                UserOrderBO.class,
                // 校验导入的BO对象的逻辑
                (bo) -> true,
                // BO转PO的逻辑，在此处实现
                (bo) -> {
                    UserOrder entity = new UserOrder();
                    BeanUtils.copyProperties(bo, entity);
                    return entity;
                },
                userOrderService);

        // 多线程解析，多线程批量插入 每个sheet对应一个线程
        // TODO 这里写死为固定4个线程各自处理1个sheet，后续可以自行更改
        for (int i = 0; i < 4; i++) {
            int sheetNo = i;
            CompletableFuture<Void> parseFuture = CompletableFuture.runAsync(() -> {
                try {
                    log.info("线程: {} 开始解析第 {} 个 sheet页的数据", Thread.currentThread().getName(), sheetNo);
//                    EasyExcel.read(file.getInputStream(), UserOrderBO.class, userOrderListener)
                    EasyExcel.read(file.getInputStream(), UserOrderBO.class, listener)
                            .sheet(sheetNo)
                            .doRead();

                    // 获取线程中保存的批量插入任务并添加到数据库任务列表中
                    dbFutures.addAll(listener.getDbFutureList());
                } catch (IOException e) {
                    log.error("解析Excel文件时出错: {}", e.getMessage());
                }
            }, importParsingThreadPool).exceptionally((error) -> {
                log.error("线程: {} 出错: {}", Thread.currentThread().getName(), error.getMessage());
                return null;
            });
            // 添加到解析任务列表中
            parseFutures.add(parseFuture);
        }

        // 等待所有线程完成
        log.info("解析任务数量: {} 个", parseFutures.size());

        // 第一次等待时，parseFutures.size()=4，parseFutures.size()=0，
        // 等4个解析线程(importParsingThreadPool)完成任务，解析线程工作时会往dbFutures集合添加n个批量插入线程(importThreadPool)任务
        CompletableFuture.allOf(parseFutures.toArray(new CompletableFuture[0])).join();

        log.info("数据库任务数量: {} 个", dbFutures.size());
        // 第二次等待，dbFutures.size() != 0，因为解析线程任务都执行完了，所以这时候的dbFutures就保存了所有的批量插入任务，这时候全部等待执行完
        CompletableFuture.allOf(dbFutures.toArray(new CompletableFuture[0])).join();

        log.info("导入结束： 共耗时: {} ms", (System.currentTimeMillis() - start));
    }

}
